/***************************************************************************
 *   Copyright (C) 2007by Anders Nore *
 *   andenore@start.no   *
 *                                                                         *
 *   Permission is hereby granted, free of charge, to any person obtaining *
 *   a copy of this software and associated documentation files (the       *
 *   "Software"), to deal in the Software without restriction, including   *
 *   without limitation the rights to use, copy, modify, merge, publish,   *
 *   distribute, sublicense, and/or sell copies of the Software, and to    *
 *   permit persons to whom the Software is furnished to do so, subject to *
 *   the following conditions:                                             *
 *                                                                         *
 *   The above copyright notice and this permission notice shall be        *
 *   included in all copies or substantial portions of the Software.       *
 *                                                                         *
 *   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,       *
 *   EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF    *
 *   MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. *
 *   IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR     *
 *   OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, *
 *   ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR *
 *   OTHER DEALINGS IN THE SOFTWARE.                                       *
 ***************************************************************************/

#include "unp.h"

int Socket(int domain, int type, int protocol)
{
	int ret;
	if((ret = socket(domain, type, protocol)) == -1) {
		err_quit("Socket() failed");
		return -1;
	} else {
		return ret;
	}
} 

int Bind(int s, const struct sockaddr *addr, socklen_t addrlen)
{
	if(bind(s, addr, addrlen) == -1) {
		err_sys("Bind() failed");
		return -1;
	} else 
		return 0;
}

int Listen(int s, int backlog)
{
	if(listen(s, backlog) == -1) { 
		err_sys("Listen() failed");
		return -1;
	} else
		return 0;
}

int Accept(int s, struct sockaddr *addr, socklen_t *addrlen)
{
	int ret;
	if((ret = accept(s, addr, addrlen)) == -1) {
		err_sys("Accept() failed");
		return -1;
	} else {
		return ret;
	}
}

int Connect(int s, const struct sockaddr *name, socklen_t namelen) 
{
	if(connect(s, name, namelen) == -1) {
		err_sys("Connect() failed");
		return -1;
	} else
		return 0;
}

int Shutdown(int s, int how)
{
	if(shutdown(s, how) < 0)
		err_quit("Shutdown() error");
	
	return 0;
}

ssize_t Write(int d, const void *buf, size_t nbytes)
{
	int ret;
	if((ret = write(d, buf, nbytes)) == -1) {
		err_sys("Write() failed");
		return -1;
	} else 
		return ret;
}

ssize_t Read(int d, const void *buf, size_t nbytes)
{
	int ret;
	if((ret = read(d, buf, nbytes)) == -1) {
		err_quit("Read() failed");
		return -1;
	} else 
		return ret;
}

int Select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds,
         struct timeval *timeout)
{
	int ret;
	if((ret = select(nfds, readfds, writefds, exceptfds, timeout)) == -1) {
		err_quit("Select() error");
		return -1;
	} else 
		return ret;
}

const char *Inet_ntop(socklen_t len, const void *src, char *dst, socklen_t size)
{
	const char *ret;
	
	if(len == sizeof(struct sockaddr_in)) {
		if(ret = (inet_ntop(AF_INET, src, dst, size)) == NULL) {
			err_sys( "Inet_ntop() failed" );
		}
	} else if(len == sizeof(struct sockaddr_in6)) {
		if(ret = (inet_ntop(AF_INET6, src, dst, size)) == NULL) {
			err_sys( "Inet_ntop() failed" );
		}
	}	
	return ret;
}

/**
* Converts sockaddr into human readable format, for ipv4 and ipv6
* @return string to ipv4/6 addr and port  "addr:port"
*/
const char *Sock_ntop(const struct sockaddr *sa, socklen_t salen)
{
	char portstr[8];
	static char str[128];
	
	switch(sa->sa_family) {
		case AF_INET:{
			struct sockaddr_in *sin = (struct sockaddr_in *) sa;
			if(inet_ntop(AF_INET, &sin->sin_addr, str, sizeof(str)) == NULL)
				return NULL;
			if(ntohs(sin->sin_port) != 0) {
				snprintf(portstr, sizeof(portstr), ":%d", ntohs(sin->sin_port));
				strcat(str, portstr);
			}
			
			return str;
		}
		case AF_INET6:{
			struct sockaddr_in6 *sin = (struct sockaddr_in6 *) sa;
			if(inet_ntop(AF_INET6, &sin->sin6_addr, str, sizeof(str)) == NULL)
				return NULL;
			if(ntohs(sin->sin6_port) != 0) {
				snprintf(portstr, sizeof(portstr), ":%d", ntohs(sin->sin6_port));
				strcat(str, portstr);
			}
			
			return str;
		}
	}
}

int Sock_pton(const char *string, void *dst)
{
	if( sizeof(dst) == sizeof(struct in_addr))
		return inet_pton(AF_INET, string, dst);
	else if(sizeof(dst) == sizeof(struct in6_addr))
		return inet_pton(AF_INET6, string, dst);
	else
		err_quit("Sock_pton::Error");
}

void err_quit(char *msg, ...) 
{
	va_list ap;
	va_start(ap, msg);
	printf(msg, ap);
	va_end(ap);
	exit(0);
}

void err_sys(char *msg, ...)
{
	va_list ap;
	va_start(ap, msg);
	fprintf(STDERR_FILENO, msg, ap);
	va_end(ap);
	return;
}

